package com.baoai.util;

import com.baoai.MyIkAnalyzer;
import com.baoai.dto.SearchIndexDto;
import org.apache.lucene.document.*;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.*;
import org.apache.lucene.store.FSDirectory;
import org.codehaus.jackson.map.ObjectMapper;
import org.json.JSONObject;
import org.wltea.analyzer.core.IKSegmenter;
import org.wltea.analyzer.core.Lexeme;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 索引工具类
 * Created by xiaren on 2017/3/4.
 */
public class LuenceSearchUtil {

    static String filePath = "//index//searchIndex";
    static{
        filePath = System.getProperty("user.dir") + filePath;
    }


    /**
     * 添加或更新索引
     * @param searchIndexDto
     * @throws IOException
     */
    public static void saveOrUpdateIndex(SearchIndexDto searchIndexDto) throws IOException {
        IndexWriter writer = null;
        try {

            SearchIndexDto searchIndexDtoResult = searchIndex(searchIndexDto.getId(), searchIndexDto.getType());
            IndexWriterConfig writerConfig = new IndexWriterConfig(new MyIkAnalyzer());
            writer = new IndexWriter(FSDirectory.open(Paths.get(filePath).toAbsolutePath()), writerConfig);
            if (searchIndexDtoResult != null) {
                Term term = new Term(SearchIndexDto.ID, searchIndexDto.getId());
                writer.updateDocument(term, bean2Document(searchIndexDto));
            } else {
                writer.addDocument(bean2Document(searchIndexDto));
            }
        } finally {
            if (writer != null && writer.isOpen()) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 复杂查询
     *
     * @param searchArray
     * @param type
     * @param pageIndex
     * @param pageSize
     * @return
     */
    public static List<SearchIndexDto> complexearchIndex(String[] searchArray, String type, int pageIndex, int pageSize) throws IOException {
        DirectoryReader ireader = null;
        List<SearchIndexDto> textDtoList = new ArrayList<>(pageSize);
        File file = new File(filePath);
        if (!file.exists()) {
            return textDtoList;
        }
        try {
            ireader = DirectoryReader.open(FSDirectory.open(Paths.get(filePath)));
            IndexSearcher searcher = new IndexSearcher(ireader);

            Sort sort = new Sort(new SortField[]
                    {
                            // 按照相关度查询，然后再按照权重排序
                            new SortField(SearchIndexDto.POWER, SortField.Type.INT, false)//注释掉这个就是按照相关度排序
                    }
            );

            DisjunctionMaxQuery disjunctionMaxQuery = null;
            List<Query> collectQuery = new ArrayList<>();
            collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.TITLE, searchArray)).build());
            if ("0".equals(type)) {   // 综合查询
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.CONTENT, searchArray)).build());
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.LABEL_0, searchArray)).build());  // 标签最多三个，就这样了
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.LABEL_1, searchArray)).build());
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.LABEL_2, searchArray)).build());
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.AUTHOR, searchArray)).build());
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.TITLE_NAME, searchArray)).build());
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.ADDRESS, searchArray)).build());
            } else if ("1".equals(type)) {   // 视频
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.LABEL_0, searchArray)).build());  // 标签最多三个，就这样了
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.LABEL_1, searchArray)).build());
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.LABEL_2, searchArray)).build());
            } else if ("2".equals(type)) {   // 问答
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.LABEL_0, searchArray)).build());  // 话题类型名称
            } else if ("3".equals(type)) {   // 课程
            } else if ("4".equals(type)) {   // 文章
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.CONTENT, searchArray)).build());
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.LABEL_0, searchArray)).build());  // 标签最多三个，就这样了
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.LABEL_1, searchArray)).build());
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.LABEL_2, searchArray)).build());
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.AUTHOR, searchArray)).build());
            } else if ("5".equals(type)) {   // 专家
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.LABEL_0, searchArray)).build());  // 标签最多三个，就这样了
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.LABEL_1, searchArray)).build());
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.LABEL_2, searchArray)).build());
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.AUTHOR, searchArray)).build());
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.TITLE_NAME, searchArray)).build());
                collectQuery.add(new MultiPhraseQuery.Builder().add(buildTerms(SearchIndexDto.ADDRESS, searchArray)).build());
            }

            disjunctionMaxQuery = new DisjunctionMaxQuery(collectQuery, 0.1F);

            Query query = null;
            if ("0".equals(type)) {
                query = disjunctionMaxQuery;
            } else {
                Query queryType = new TermQuery(new Term(SearchIndexDto.TYPE, type));
                BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder();
                booleanQuery.add(disjunctionMaxQuery, BooleanClause.Occur.MUST);
                booleanQuery.add(queryType, BooleanClause.Occur.MUST);
                query = booleanQuery.build();
            }

            TopDocs rs = searcher.search(query, 100000, sort);      // 一次总共查询10万条数据，内存应该不会暴

            int start = (pageIndex - 1) * pageSize;
            int end = pageIndex * pageSize;
            end = end > rs.scoreDocs.length ? rs.scoreDocs.length : end;
            for (int i = start; i < end; i++) {
                Document firstHit = searcher.doc(rs.scoreDocs[i].doc);
                textDtoList.add(doc2Bean(firstHit));
            }
            System.out.println("=============================================================");
        } finally {
            try {
                if (ireader != null && ireader.isCurrent()) {
                    ireader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return textDtoList;
    }

    /**
     * 索引文件转bean
     * @param firstHit
     * @return
     * @throws IOException
     */
    private static SearchIndexDto doc2Bean(Document firstHit) throws IOException {
        SearchIndexDto searchIndexDto = new SearchIndexDto();
        searchIndexDto.setTitleName(firstHit.get(SearchIndexDto.TITLE_NAME));
        searchIndexDto.setTitle(firstHit.get(SearchIndexDto.TITLE));
        searchIndexDto.setAddress(firstHit.get(SearchIndexDto.ADDRESS));
        searchIndexDto.setAuthor(firstHit.get(SearchIndexDto.AUTHOR));
        searchIndexDto.setContent(firstHit.get(SearchIndexDto.CONTENT));
        searchIndexDto.setType(firstHit.get(SearchIndexDto.TYPE));
        searchIndexDto.setSrcArrays(json2Arrays(firstHit.get(SearchIndexDto.SRC_ARRAYS)));
        searchIndexDto.setId(firstHit.get(SearchIndexDto.ID));

        String number = firstHit.get(SearchIndexDto.NUMBER);
        if (number != null) {
            searchIndexDto.setNumber(Integer.valueOf(number));
        }
        String presentPrice = firstHit.get(SearchIndexDto.PRESET_PRICE);
        if (presentPrice != null) {
            searchIndexDto.setPresentPrice(Integer.valueOf(presentPrice));
        }
        String oldPrice = firstHit.get(SearchIndexDto.OLD_PRICE);
        if (oldPrice != null) {
            searchIndexDto.setOldPrice(Integer.valueOf(oldPrice));
        }
        String power = firstHit.get(SearchIndexDto.POWER);
        if (power != null) {
            searchIndexDto.setPower(Integer.valueOf(power));
        }
        String time = firstHit.get(SearchIndexDto.TIME);
        if (time != null) {
            searchIndexDto.setTime(Integer.valueOf(time));
        }
        String topicId = firstHit.get(SearchIndexDto.TOPIC_ID);
        if (topicId != null) {
            searchIndexDto.setTopicId(Integer.valueOf(topicId));
        }
        if (firstHit.get(SearchIndexDto.LABEL_) != null) {
            Integer labelNumber = Integer.valueOf(firstHit.get(SearchIndexDto.LABEL_));
            String[] arrayLabel = new String[labelNumber];
            for (int i = 0; i < labelNumber; i++) {
                arrayLabel[i] = firstHit.get(SearchIndexDto.LABEL_ + i);
            }
            searchIndexDto.setLabelArrays(arrayLabel);
        }

        System.out.println(searchIndexDto.toString());
        return searchIndexDto;
    }

    /**
     * 根据id，type精确查询
     *
     * @param id
     * @param type
     * @return
     */
    public static SearchIndexDto searchIndex(String id, String type) throws IOException {
        DirectoryReader ireader = null;
        SearchIndexDto searchIndexDto = null;
        try {
            File file = new File(filePath);
            if (!file.exists()) {
                return null;
            }
            ireader = DirectoryReader.open(FSDirectory.open(Paths.get(filePath)));
            IndexSearcher searcher = new IndexSearcher(ireader);
            Query query1 = new TermQuery(new Term(SearchIndexDto.ID, id));
            Query query2 = new TermQuery(new Term(SearchIndexDto.TYPE, type));

            BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder();
            booleanQuery.add(query1, BooleanClause.Occur.MUST);
            booleanQuery.add(query2, BooleanClause.Occur.MUST);

            TopDocs rs = searcher.search(booleanQuery.build(), 1);
            if (rs.scoreDocs.length > 0) {
                Document firstHit = searcher.doc(rs.scoreDocs[0].doc);
                searchIndexDto = doc2Bean(firstHit);
            }
        } finally {
            try {
                if (ireader != null && ireader.isCurrent()) {
                    ireader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return searchIndexDto;
    }

    /**
     * 精确删除
     *
     * @param id
     * @param type
     */
    public static void deleteSearchIndex(String id, String type) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            return ;
        }
        IndexWriter writer = null;
        try {
            IndexWriterConfig writerConfig = new IndexWriterConfig(new MyIkAnalyzer());
            writer = new IndexWriter(FSDirectory.open(Paths.get(filePath).toAbsolutePath()), writerConfig);
            Query query1 = new TermQuery(new Term(SearchIndexDto.ID, id));
            Query query2 = new TermQuery(new Term(SearchIndexDto.TYPE, type));

            BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder();
            booleanQuery.add(query1, BooleanClause.Occur.MUST);
            booleanQuery.add(query2, BooleanClause.Occur.MUST);

            writer.deleteDocuments(booleanQuery.build());
        } finally {
            if (writer != null && writer.isOpen()) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * 查询所有
     *
     * @param type
     * @return
     */
    public static List<SearchIndexDto> searchAllIndex(String type) throws IOException {
        List<SearchIndexDto> textDtoList = new ArrayList<>();
        DirectoryReader ireader = null;
        try {
            File file = new File(filePath);
            if (!file.exists()) {
                return null;
            }
            ireader = DirectoryReader.open(FSDirectory.open(Paths.get(filePath)));
            IndexSearcher searcher = new IndexSearcher(ireader);
            Query query = new TermQuery(new Term(SearchIndexDto.TYPE, type));
            if ("0".equals(type)) {
                query = new MatchAllDocsQuery();
            }
            TopDocs rs = searcher.search(query, 10000);
            for (int i = 0; i < rs.scoreDocs.length; i++) {
                Document firstHit = searcher.doc(rs.scoreDocs[i].doc);
                textDtoList.add(doc2Bean(firstHit));
            }
        } finally {
            try {
                if (ireader != null && ireader.isCurrent()) {
                    ireader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return textDtoList;
    }

    /**
     * 使用IK分词器将词语分组
     *
     * @param text
     * @return
     * @throws IOException
     */
    public static String[] ikSeqWord(String text) throws IOException {
        StringReader sr = null;
        List<String> list = null;
        String[] array = null;
        try {
            sr = new StringReader(text);
            IKSegmenter ik = new IKSegmenter(sr, true);
            Lexeme lex = null;
            list = new ArrayList<>();
            while ((lex = ik.next()) != null) {
                list.add(lex.getLexemeText());
            }
            array = new String[list.size()];
        } finally {
            sr.close();
        }
        return list.toArray(array);
    }


    /**
     * 创建Terms数组
     *
     * @param fieldName
     * @param searchArray
     * @return
     */
    private static Term[] buildTerms(String fieldName, String[] searchArray) {
        Term[] terms = new Term[searchArray.length];
        for (int i = 0; i < searchArray.length; i++) {
            terms[i] = new Term(fieldName, searchArray[i]);
        }
        return terms;
    }

    /**
     * 用于添加权重
     *
     * @param document
     * @param name
     * @param value
     */
    private static void addIntPoint(Document document, String name, int value) {
        Field field = new IntPoint(name, value);
        document.add(field);
        //要排序，必须添加一个同名的NumericDocValuesField
        field = new NumericDocValuesField(name, value);
        document.add(field);
        //要存储值，必须添加一个同名的StoredField
        field = new StoredField(name, value);
        document.add(field);
    }

    /**
     * 将bean中的数据存储到document中
     *
     * @param searchIndexDto
     * @return
     */
    private static Document bean2Document(SearchIndexDto searchIndexDto) {
        Document doc = new Document();
        doc.add(new Field(SearchIndexDto.ID, searchIndexDto.getId(), TextField.TYPE_STORED));
        doc.add(new Field(SearchIndexDto.TITLE, searchIndexDto.getTitle(), TextField.TYPE_STORED));
        doc.add(new Field(SearchIndexDto.SRC_ARRAYS, arrays2Json(searchIndexDto.getSrcArrays()), TextField.TYPE_STORED));
        doc.add(new Field(SearchIndexDto.TYPE, searchIndexDto.getType(), TextField.TYPE_STORED));

        if ("1".equals(searchIndexDto.getType())) {    // 视频
            addIntPoint(doc, SearchIndexDto.NUMBER, searchIndexDto.getNumber());
            addIntPoint(doc, SearchIndexDto.TIME, searchIndexDto.getTime());

            addLabelField(doc, searchIndexDto.getLabelArrays());
        } else if ("2".equals(searchIndexDto.getType())) { // 问答
            doc.add(new Field(SearchIndexDto.CONTENT, searchIndexDto.getContent(), TextField.TYPE_STORED));
            addIntPoint(doc, SearchIndexDto.POWER, searchIndexDto.getPower());
            doc.add(new StoredField(SearchIndexDto.NUMBER, searchIndexDto.getNumber()));
            doc.add(new StoredField(SearchIndexDto.TOPIC_ID, searchIndexDto.getTopicId()));

        } else if ("3".equals(searchIndexDto.getType())) { // 课程
            addIntPoint(doc, SearchIndexDto.POWER, searchIndexDto.getPower());

            addIntPoint(doc, SearchIndexDto.NUMBER, searchIndexDto.getNumber());
            addIntPoint(doc, SearchIndexDto.PRESET_PRICE, searchIndexDto.getPresentPrice());
            addIntPoint(doc, SearchIndexDto.OLD_PRICE, searchIndexDto.getOldPrice());

        } else if ("4".equals(searchIndexDto.getType())) { // 文章
            addIntPoint(doc, SearchIndexDto.POWER, searchIndexDto.getPower());
            doc.add(new Field(SearchIndexDto.AUTHOR, searchIndexDto.getAuthor(), TextField.TYPE_STORED));
            addLabelField(doc, searchIndexDto.getLabelArrays());

        } else if ("5".equals(searchIndexDto.getType())) { // 专家
            addIntPoint(doc, SearchIndexDto.POWER, searchIndexDto.getPower());
            addIntPoint(doc, SearchIndexDto.NUMBER, searchIndexDto.getNumber());
            doc.add(new Field(SearchIndexDto.ADDRESS, searchIndexDto.getAddress(), TextField.TYPE_STORED));
            doc.add(new Field(SearchIndexDto.TITLE_NAME, searchIndexDto.getTitleName(), TextField.TYPE_STORED));
            addLabelField(doc, searchIndexDto.getLabelArrays());
        }
        return doc;
    }

    /**
     * 添加标签类型索引
     * @param doc
     * @param labelArray
     */
    private static void addLabelField(Document doc, String[] labelArray) {
        if (labelArray != null && labelArray.length > 0) {
            for (int i = 0; i < labelArray.length; i++) {
                doc.add(new Field(SearchIndexDto.LABEL_ + i, labelArray[i], TextField.TYPE_STORED));
            }
            doc.add(new Field(SearchIndexDto.LABEL_, String.valueOf(labelArray.length), TextField.TYPE_STORED));
        }
    }

    /**
     * 数组转json
     * @param srcArrays
     * @return
     */
    public static String arrays2Json(String[] srcArrays) {
        if (srcArrays != null || srcArrays.length > 0) {
            return JSONObject.valueToString(srcArrays);
        }
        return null;
    }

    /**
     * json转数组
     * @param json
     * @return
     * @throws IOException
     */
    public static String[] json2Arrays(String json) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        if (json != null && json.length() > 0) {
            return mapper.readValue(json, String[].class);
        }
        return null;
    }

    /**
     * 判断字符串是否能转换成数字
     * @param str
     * @return
     */
    public static boolean isNumeric(String str) {
        Pattern pattern = Pattern.compile("[0-9]*");
        Matcher isNum = pattern.matcher(str);
        if (!isNum.matches()) {
            return false;
        }
        return true;
    }

    /**
     * string类型转int类型
     * @param str
     * @return
     */
    public static Integer int2String(String str) {
        return str == null ? null : Integer.valueOf(str);
    }

    /**
     * bean转json
     * @param obj
     * @return
     * @throws IOException
     */
    public static String bean2Json(Object obj) throws IOException {
        StringWriter sw = new StringWriter();
        ObjectMapper mapper = new ObjectMapper();
        mapper.writeValue(sw, obj);
        return sw.toString();
    }


}
